package com.example.mockclient;

import org.springframework.http.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.zip.DataFormatException;

public class RateLimitTestMockClient {

    static String qps10 = "http://localhost:10001/v1/other/hello?name=wilson";
    static String qps5 = "http://localhost:10001/v1/x01/hello?name=wilson";
    static String qps3 = "http://localhost:10001/v1/x02/hello?name=wilson";
    static HttpHeaders httpHeaders = new HttpHeaders();
    static HttpEntity<String> entity = new HttpEntity<>(httpHeaders);

    static RestTemplate restTemplate = new RestTemplate();

    static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws Exception {
        callApi(qps3);
        shutdown();
    }

    private static void callApi(String url) throws Exception {
        List<Response> responses = execRateLimitRequests(url, 20, 3);
        System.out.println( url + ":");
        for (Response response : responses) {
            System.out.println((new Date(response.getTimestamp())) + " : " + response.status + " : " + response.resp);
        }
    }

    private static List<Response> execRateLimitRequests(String url, int taskNum, int execTimes) throws Exception{
        warm(url);
        System.out.println("start to exec " + url);
        List<Future<Response>> fs = new ArrayList<>();
        List<Task> tasks = generateTasks(taskNum, url);
        for(int i = 0; i < execTimes; i++){
            fs.addAll(executor.invokeAll(tasks));
            TimeUnit.SECONDS.sleep(1);
        }
        List<Response> list = new ArrayList<>(execTimes * taskNum);
        for (Future<Response> f : fs) {
            list.add(f.get());
        }

        list.sort(Comparator.comparingLong(Response::getTimestamp));
        return list.stream().filter( r -> r.resp != null).collect(Collectors.toList());
    }

    private static void warm(String url){
        System.out.println("warmup " + url);
        List<Response> list = new ArrayList<>();
        generateTasks(10, url).forEach(task -> {
            Future<Response> fs = executor.submit(task);
            try {
                list.add(fs.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        list.forEach(response -> System.out.println(response.toString()));
    }

    public static void shutdown(){
        executor.shutdown();
    }

    private static List<Task> generateTasks(int num, String url) {
        List<Task> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(new Task(url));
        }
        return list;
    }

    static class Response {
        long timestamp;
        HttpStatus status;
        String resp;

        public Response(long timestamp, HttpStatus status, String resp) {
            this.timestamp = timestamp;
            this.status = status;
            this.resp = resp;
        }

        public long getTimestamp() {
            return timestamp;
        }
    }

    static class Task implements Callable<Response> {

        private final String url;

        public Task(String url) {
            this.url = url;
        }

        @Override
        public Response call() throws Exception {
            long timestamp = System.currentTimeMillis();
            try {
                ResponseEntity<String> rsp = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
                return new Response(timestamp, rsp.getStatusCode(), rsp.getBody());
            } catch (HttpClientErrorException.TooManyRequests e) {
                return new Response(timestamp, HttpStatus.TOO_MANY_REQUESTS, null);
            }
        }
    }

    static class IntVal{
        int val;

        public void increment(){
            val++;
        }
    }
}
